package cn.com.anypay.manager.lakala.controller;

import cn.com.anypay.manager.common.response.ApiResponse;
import cn.com.anypay.manager.lakala.config.LakalaConfig;
import cn.com.anypay.manager.lakala.dto.*;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import jakarta.validation.Valid;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 拉卡拉电子合同管理控制器
 *
 * @author Claude
 * @date 2024-01-15
 */
@Slf4j
@RestController
@RequestMapping("/api/lakala/contract")
@RequiredArgsConstructor
@Tag(name = "拉卡拉电子合同", description = "拉卡拉电子合同申请、查询、下载等接口")
@Validated
public class LakalaContractController {

    private final LakalaConfig lakalaConfig;
    private final ObjectMapper objectMapper;

    /**
     * 申请电子合同
     */
    @PostMapping("/apply")
    @Operation(summary = "申请电子合同", description = "提交拉卡拉电子合同申请")
    public ApiResponse<LakalaContractApplyResponse> applyContract(
            @Valid @RequestBody LakalaContractApplyRequest request) {

        log.info("[合同申请] 开始处理，订单号：{}，商户名：{}，手机号：{}",
            request.getOrderNo(), request.getCertName(), request.getMobile());

        try {
            // 1. 参数校验和补充
            validateAndFillRequest(request);

            // 2. 构建请求数据
            Map<String, Object> requestData = buildRequestData(request);

            // 3. 构建请求头（包含Authorization签名）
            Map<String, String> headers = buildHeaders(requestData);
            log.info("[合同申请] 生成的请求头：{}", headers);

            // 4. 发送HTTP请求到拉卡拉API
            String url = lakalaConfig.getContractApplyUrl();
            log.info("[合同申请] 调用拉卡拉API，URL：{}", url);
            log.info("[合同申请] 请求数据：{}", JSONUtil.toJsonStr(requestData));

            HttpResponse response = HttpRequest.post(url)
                    .headerMap(headers, true)
                    .body(JSONUtil.toJsonStr(requestData))
                    .timeout(lakalaConfig.getTimeout() * 1000)
                    .execute();

            String responseBody = response.body();
            log.info("[合同申请] API响应状态码：{}, 响应内容：{}", response.getStatus(), responseBody);

            // 5. 解析响应
            LakalaContractApplyResponse applyResponse = parseApplyResponse(responseBody);

            if ("000000".equals(applyResponse.getCode())) {
                log.info("[合同申请] ✓ 申请成功，订单号：{}，受理编号：{}，签约链接：{}",
                    request.getOrderNo(),
                    applyResponse.getRespData() != null ? applyResponse.getRespData().getEcApplyId() : null,
                    applyResponse.getRespData() != null ? applyResponse.getRespData().getResultUrl() : null);
                return ApiResponse.success(applyResponse);
            } else {
                log.warn("[合同申请] ✗ 申请失败，订单号：{}，错误码：{}，错误信息：{}",
                    request.getOrderNo(), applyResponse.getCode(), applyResponse.getMessage());
                return ApiResponse.fail(applyResponse.getMessage());
            }

        } catch (Exception e) {
            log.error("[合同申请] 异常，订单号：{}", request.getOrderNo(), e);

            LakalaContractApplyResponse errorResponse = new LakalaContractApplyResponse();
            errorResponse.setCode("999999");
            errorResponse.setMessage("系统异常：" + e.getMessage());

            return ApiResponse.fail("系统异常：" + e.getMessage());
        }
    }

    /**
     * 查询合同状态
     */
    @GetMapping("/query/{orderNo}")
    @Operation(summary = "查询合同状态", description = "根据订单号查询电子合同状态")
    public ApiResponse<Map<String, Object>> queryContract(
            @PathVariable @Parameter(description = "订单号") String orderNo) {

        log.info("[合同查询] 开始查询，订单号：{}", orderNo);

        try {
            // 构建内部请求数据
            Map<String, Object> innerData = new HashMap<>();
            innerData.put("order_no", orderNo);
            innerData.put("org_id", lakalaConfig.getDefaultOrgId());
            innerData.put("version", "1.0");

            // V3 API需要req_data包装
            Map<String, Object> queryData = new HashMap<>();
            queryData.put("req_data", innerData);

            // 构建请求头
            Map<String, String> headers = buildHeaders(queryData);

            // 发送查询请求
            String url = lakalaConfig.getContractQueryUrl();
            HttpResponse response = HttpRequest.post(url)
                    .headerMap(headers, true)
                    .body(JSONUtil.toJsonStr(queryData))
                    .timeout(lakalaConfig.getTimeout() * 1000)
                    .execute();

            String responseBody = response.body();
            log.info("[合同查询] API响应：{}", responseBody);

            // 解析响应
            Map<String, Object> result = objectMapper.readValue(responseBody, Map.class);

            if ("000000".equals(result.get("code"))) {
                log.info("[合同查询] ✓ 查询成功，订单号：{}", orderNo);

                // 提取关键信息
                Map<String, Object> data = new HashMap<>();
                data.put("orderNo", orderNo);
                data.put("status", result.get("ec_status"));
                data.put("ecNo", result.get("ec_no"));
                data.put("signTime", result.get("sign_time"));
                data.put("certName", result.get("cert_name"));
                data.put("mobile", result.get("mobile"));

                return ApiResponse.success(data);
            } else {
                return ApiResponse.fail((String) result.get("message"));
            }

        } catch (Exception e) {
            log.error("[合同查询] 异常，订单号：{}", orderNo, e);
            return ApiResponse.fail("查询失败：" + e.getMessage());
        }
    }

    /**
     * 下载合同文件
     */
    @GetMapping("/download/{ecNo}")
    @Operation(summary = "下载合同", description = "根据合同号下载电子合同PDF文件")
    public ResponseEntity<?> downloadContract(
            @PathVariable @Parameter(description = "电子合同号") String ecNo) {

        log.info("[合同下载] 开始下载，合同号：{}", ecNo);

        try {
            // 构建内部请求数据
            Map<String, Object> innerData = new HashMap<>();
            innerData.put("ec_no", ecNo);
            innerData.put("org_id", lakalaConfig.getDefaultOrgId());
            innerData.put("version", "1.0");

            // V3 API需要req_data包装
            Map<String, Object> downloadData = new HashMap<>();
            downloadData.put("req_data", innerData);

            // 构建请求头
            Map<String, String> headers = buildHeaders(downloadData);

            // 发送下载请求
            String url = lakalaConfig.getContractDownloadUrl();
            HttpResponse response = HttpRequest.post(url)
                    .headerMap(headers, true)
                    .body(JSONUtil.toJsonStr(downloadData))
                    .timeout(60000) // 下载可能需要更长时间
                    .execute();

            if (response.getStatus() == HttpStatus.HTTP_OK) {
                String responseBody = response.body();

                // 检查是否为错误响应
                if (responseBody.startsWith("{") && responseBody.contains("code")) {
                    Map<String, Object> result = objectMapper.readValue(responseBody, Map.class);
                    return ResponseEntity.badRequest().body(result);
                }

                // 假设返回的是Base64编码的PDF
                byte[] pdfBytes = Base64.decode(responseBody);

                log.info("[合同下载] ✓ 下载成功，合同号：{}", ecNo);

                return ResponseEntity.ok()
                        .contentType(MediaType.APPLICATION_PDF)
                        .header("Content-Disposition", "attachment; filename=contract_" + ecNo + ".pdf")
                        .body(pdfBytes);
            } else {
                log.error("[合同下载] 失败，状态码：{}", response.getStatus());
                return ResponseEntity.status(response.getStatus())
                        .body(Map.of("error", "下载失败", "status", response.getStatus()));
            }

        } catch (Exception e) {
            log.error("[合同下载] 异常，合同号：{}", ecNo, e);
            return ResponseEntity.internalServerError()
                    .body(Map.of("error", "下载失败：" + e.getMessage()));
        }
    }

    /**
     * 生成订单号
     */
    @GetMapping("/generate-order-no")
    @Operation(summary = "生成订单号", description = "生成唯一的订单号")
    public ApiResponse<String> generateOrderNo() {
        String timestamp = DateUtil.format(new Date(), "yyyyMMddHHmmss");
        String random = IdUtil.fastSimpleUUID().substring(0, 8).toUpperCase();
        String orderNo = "EC" + timestamp + random;

        log.info("[订单号生成] 生成新订单号：{}", orderNo);
        return ApiResponse.success(orderNo);
    }

    /**
     * 获取拉卡拉公钥（测试接口）
     */
    @GetMapping("/public-key/{appId}")
    @Operation(summary = "获取公钥", description = "根据appId获取拉卡拉公钥（用于测试）")
    public ApiResponse<Map<String, Object>> getPublicKey(
            @PathVariable @Parameter(description = "应用ID") String appId) {

        log.info("[公钥获取] 尝试获取公钥，appId：{}", appId);

        Map<String, Object> result = new HashMap<>();
        result.put("appId", appId);

        // 测试不同环境
        Map<String, Object> responses = new HashMap<>();

        // 测试环境
        String testUrl = "https://test.wsmsd.cn/sit/gw/pubcerts?appId=" + appId;
        responses.put("testEnv", testGetPublicKeyFromUrl(testUrl, "测试环境"));

        // 生产环境
        String prodUrl = "https://s2.lakala.com/gw/pubcerts?appId=" + appId;
        responses.put("prodEnv", testGetPublicKeyFromUrl(prodUrl, "生产环境"));

        // 路径格式
        String testPathUrl = "https://test.wsmsd.cn/sit/gw/pubcerts/" + appId;
        responses.put("testPathEnv", testGetPublicKeyFromUrl(testPathUrl, "测试环境(路径格式)"));

        result.put("responses", responses);

        // 检查是否有成功的
        boolean hasSuccess = responses.values().stream()
                .anyMatch(resp -> resp instanceof Map && "000000".equals(((Map) resp).get("code")));

        if (hasSuccess) {
            result.put("success", true);
            result.put("message", "成功获取公钥");
        } else {
            result.put("success", false);
            result.put("message", "所有环境均无法获取公钥，可能需要签名认证");
        }

        return ApiResponse.success(result);
    }

    // ==================== 私有辅助方法 ====================

    /**
     * 验证和填充请求参数
     */
    private void validateAndFillRequest(LakalaContractApplyRequest request) {
        // 生成订单号（如果没有）
        if (StrUtil.isBlank(request.getOrderNo())) {
            String timestamp = DateUtil.format(new Date(), "yyyyMMddHHmmss");
            String random = IdUtil.fastSimpleUUID().substring(0, 8).toUpperCase();
            request.setOrderNo("EC" + timestamp + random);
        }

        // 设置默认机构号
        if (request.getOrgId() == null) {
            request.setOrgId(lakalaConfig.getDefaultOrgId());
        }

        // 构建EC参数（如果没有）
        if (StrUtil.isBlank(request.getEcContentParameters())) {
            EC008Parameters params = buildEC008Parameters(request);
            try {
                request.setEcContentParameters(objectMapper.writeValueAsString(params));
            } catch (Exception e) {
                log.error("序列化EC参数失败", e);
                request.setEcContentParameters("{}");
            }
        }

        // 设置版本号
        request.setVersion("1.0");
    }

    /**
     * 构建EC008参数
     */
    private EC008Parameters buildEC008Parameters(LakalaContractApplyRequest request) {
        EC008Parameters params = new EC008Parameters();

        // 设置基础参数
        Date now = new Date();
        params.setA1(StrUtil.isNotBlank(request.getBusinessLicenseName()) ?
            request.getBusinessLicenseName() : request.getCertName());
        params.setA96("T1"); // 默认T1结算

        // 设置日期参数
        params.setA104(DateUtil.year(now));
        params.setA105(DateUtil.month(now) + 1);
        params.setA106(DateUtil.dayOfMonth(now));
        params.setA107(DateUtil.year(now));
        params.setA108(DateUtil.month(now) + 1);
        params.setA109(DateUtil.dayOfMonth(now));

        // 设置商户信息
        params.setB1(DateUtil.year(now));
        params.setB2(DateUtil.month(now) + 1);
        params.setB8(request.getBusinessLicenseName());
        params.setB9("餐饮服务"); // 默认主营业务
        params.setB10(StrUtil.isNotBlank(request.getBusinessLicenseName()) ?
            request.getBusinessLicenseName() : request.getCertName());
        params.setB14(request.getBusinessLicenseNo());
        params.setB16(request.getAcctName());
        params.setB19(request.getOpenningBankName());
        params.setB20(request.getAcctNo());
        params.setB24(request.getCertName());
        params.setB25(request.getCertNo());
        params.setB26(request.getMobile());
        params.setB27(request.getCertName());
        params.setB29(request.getCertNo());
        params.setB30(request.getMobile());

        // 设置授权信息
        String dateStr = DateUtil.format(now, "yyyy/MM/dd");
        params.setD1(dateStr);
        params.setD2(request.getCertName());
        params.setD4(params.getA1());
        params.setD5(request.getCertName());
        params.setD7(dateStr);
        params.setD12(dateStr);

        // 设置费率（默认值）
        params.setA29("0.38"); // 支付宝费率
        params.setA30("0.38"); // 微信费率
        params.setA31("0.38"); // 京东费率

        return params;
    }

    /**
     * 构建请求数据（转换为拉卡拉API格式）
     */
    private Map<String, Object> buildRequestData(LakalaContractApplyRequest request) {
        // 构建内部请求数据
        Map<String, Object> innerData = objectMapper.convertValue(request, Map.class);

        // 添加商户号和终端号（V3测试环境需要）
        innerData.put("merchant_no", lakalaConfig.getMerchantNo());
        innerData.put("term_no", lakalaConfig.getTerminalNo());

        // V3 API需要req_data包装
        Map<String, Object> requestData = new HashMap<>();
        requestData.put("req_data", innerData);

        log.info("[请求构建] 请求数据: {}", JSONUtil.toJsonStr(requestData));

        return requestData;
    }

    /**
     * 构建请求头（包含Authorization签名）
     */
    private Map<String, String> buildHeaders(Map<String, Object> requestData) {
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        headers.put("Accept", "application/json");

        // 改为始终生成签名，即使在调试模式
        // if (lakalaConfig.isDebugMode()) {
        //     log.info("[签名] Debug模式，跳过签名生成");
        //     return headers;
        // }

        // 生成Authorization（如果配置了必要参数）
        String authorization = generateAuthorization(requestData);
        if (StrUtil.isNotBlank(authorization)) {
            headers.put("Authorization", authorization);
        }

        // 添加拉卡拉证书序列号（如果配置了）
        String lklSerialNo = lakalaConfig.getLklSerialNo();
        if (StrUtil.isNotBlank(lklSerialNo)) {
            headers.put("Lklapi-Serial", lklSerialNo);
        }

        return headers;
    }

    /**
     * 生成Authorization签名
     * 格式：LKLAPI-SHA256withRSA appid="${appid}",serial_no="${serialNo}",timestamp="${timeStamp}",nonce_str="${nonceStr}",signature="${signature}"
     */
    private String generateAuthorization(Map<String, Object> requestData) {
        try {
            // 获取配置参数
            String appId = lakalaConfig.getAppId();
            String serialNo = lakalaConfig.getSerialNo();
            String privateKey = lakalaConfig.getPrivateKey();

            // 参数验证
            if (StrUtil.isBlank(appId) || StrUtil.isBlank(serialNo) || StrUtil.isBlank(privateKey)) {
                log.warn("[签名] 缺少必要的配置参数: appId={}, serialNo={}, privateKey配置={}",
                    appId, serialNo, StrUtil.isNotBlank(privateKey));
                return null;
            }

            // 生成时间戳（秒级）
            long timestamp = System.currentTimeMillis() / 1000;

            // 生成12位随机字符串
            String nonceStr = RandomUtil.randomString(12);

            // 请求body的JSON字符串
            String body = JSONUtil.toJsonStr(requestData);

            // 构建待签名字符串（每行以\n结尾，包括最后一行）
            // 注意：官方文档强调最后一行也必须有换行符，这是90%以上签名错误的原因
            StringBuilder signBuilder = new StringBuilder();
            signBuilder.append(appId).append("\n");
            signBuilder.append(serialNo).append("\n");
            signBuilder.append(timestamp).append("\n");
            signBuilder.append(nonceStr).append("\n");
            signBuilder.append(body).append("\n");

            String signStr = signBuilder.toString();

            // 打印待签名字符串的详细信息用于调试
            log.info("[签名] 待签名字符串长度: {}", signStr.length());
            log.info("[签名] 待签名字符串内容:\n{}", signStr);
            log.info("[签名] 最后一个字符ASCII码: {}", (int)signStr.charAt(signStr.length() - 1));
            log.info("[签名] AppId: {}, SerialNo: {}, Timestamp: {}, NonceStr: {}",
                appId, serialNo, timestamp, nonceStr);
            log.info("[签名] Body: {}", body);

            // 进行RSA签名
            String signature = signWithRSA(signStr, privateKey);

            // 构建Authorization字段
            String authorization = String.format(
                "LKLAPI-SHA256withRSA appid=\"%s\",serial_no=\"%s\",timestamp=\"%s\",nonce_str=\"%s\",signature=\"%s\"",
                appId, serialNo, timestamp, nonceStr, signature
            );

            log.debug("[签名] 生成的Authorization: {}", authorization);
            return authorization;

        } catch (Exception e) {
            log.error("[签名] 生成签名失败", e);
            return null;
        }
    }

    /**
     * RSA签名
     */
    private String signWithRSA(String data, String privateKeyStr) throws Exception {
        // 移除私钥中的头尾标记和换行符
        privateKeyStr = privateKeyStr
            .replace("-----BEGIN PRIVATE KEY-----", "")
            .replace("-----END PRIVATE KEY-----", "")
            .replace("-----BEGIN RSA PRIVATE KEY-----", "")
            .replace("-----END RSA PRIVATE KEY-----", "")
            .replaceAll("\\s", "");

        // 解码私钥
        byte[] keyBytes = Base64.decode(privateKeyStr);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);

        // 签名
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(data.getBytes(StandardCharsets.UTF_8));

        // Base64编码
        return Base64.encode(signature.sign());
    }

    /**
     * 解析申请响应
     */
    private LakalaContractApplyResponse parseApplyResponse(String responseBody) {
        try {
            return objectMapper.readValue(responseBody, LakalaContractApplyResponse.class);
        } catch (Exception e) {
            log.error("解析响应失败：{}", responseBody, e);

            LakalaContractApplyResponse errorResponse = new LakalaContractApplyResponse();
            errorResponse.setCode("999998");
            errorResponse.setMessage("响应解析失败");
            return errorResponse;
        }
    }

    /**
     * 测试获取公钥（辅助方法）
     */
    private Map<String, Object> testGetPublicKeyFromUrl(String url, String envName) {
        Map<String, Object> result = new HashMap<>();
        result.put("environment", envName);
        result.put("url", url);

        try {
            HttpResponse response = HttpRequest.get(url)
                    .header("Accept", "application/json")
                    .header("User-Agent", "LakalaOpenAPI/3.0")
                    .timeout(5000)
                    .execute();

            String body = response.body();
            result.put("status", response.getStatus());
            result.put("body", body);

            if (response.getStatus() == HttpStatus.HTTP_OK) {
                Map<String, Object> jsonResponse = objectMapper.readValue(body, Map.class);
                result.put("code", jsonResponse.get("code"));
                result.put("message", jsonResponse.get("message"));

                if ("GW0004".equals(jsonResponse.get("code"))) {
                    result.put("authError", true);
                    result.put("errorDetail", "访问授权不通过 - 可能需要签名认证");
                }
            }
        } catch (Exception e) {
            result.put("error", true);
            result.put("errorType", e.getClass().getSimpleName());
            result.put("errorMessage", e.getMessage());
        }

        return result;
    }
}
